Program Status Register
|
Bit 31 30 29 28 27 26 25------------2 1 0 N Z C V I F Program Counter S1 S0The flags mean:
N Negative Set if result is negative Z Zero Set if result is zero C Carry Set if carry occurs O Overflow Set if overflow occurs I IRQ Interrupt disable F FIQ Fast Interrupt disable S1 and S0 are processor mode flags: S1 S0 Mode 0 0 USR - User mode 0 1 FIQ - Fast Interrupt mode 1 0 IRQ - Interrupt mode 1 1 SVC - Supervisor mode
When R15 is used as the first operand in an instruction, only the Program Counter part of it is
available. Thus, the following instruction will copy PC out to a register and add 256 to it:
ADD R0, R15, #256
(R15 and PC mean the same thing to the BASIC assembler)
When R15 is used as the second operand, all 32 bits are accessible: the Program Counter, the flags, and the status. The following code segment will identify the current processor mode:
MOV R0, #3 ; Load a bit mask (%11) into R0 AND R0, R0, PC ; AND R15 into R0, to get the mode status CMP R0, #3 ; Compare mode with '3' (SVC) BEQ svc ; If SVC mode, branch to 'svc' CMP R0, #2 ; Compare mode with '2' (IRQ) BEQ irq ; If IRQ mode, branch to 'irq' CMP R0, #1 ; Compare mode with '1' (FIQ) BEQ fiq ; If FIQ mode, branch to 'fiq' CMP R0, #0 ; Compare mode with '0' (USR) BEQ usr ; If USR mode, branch to 'usr'
new_state = old_state EOR (1 << 28)
could be pseudocode for changing the state of the oVerflow flag. But we cannot do a simple
EORS
operation as the pipeline would cause the following two instructions to be
skipped.TEQ
does a pretend EOR (the results are not
stored anywhere). Combine this with the P
suffix, which writes bits 0, 1, and 26 to
31 of the result directly to bits 0, 1, and 26 to 31 of R15 giving you an easy way to change
the flags:
TEQP R15, bit_mask
You can only change a flag if you are in a processor mode which allows you to set that flag.
This can be expanded to change processor mode. For example, to enter SVC mode you would:
MOV R6, PC ; Store original state of PC in R6 ORR R7, R6, #3 ; Set SVC mode TEQP R7, #0 ; Write mode flags (in R7) to PCAnd to return to the original mode:
TEQP R6, #0 ; Write previous mode flags (in R6) to PC
After changing the mode, you should perform a null operation to allow the registers to settle.
Something like MOV R0, R0
should be okay. The use of NV suffixed
instructions has been deprecated.
Branch if Carry Set
, the ARM takes this a logical stage further
to mean XXX if carry set
- where XXX is just about anything.
By way of example, here is a list of branch instructions understood by the Intel 8086 processor:
JA Jump if Above JAE Jump if Above or Equal JB Jump if Below JBE Jump if Below or Equal JC Jump if Carry JCXZ Jump if CX Zero (CX is a register that can be used for loop counts) JE Jump if Equal JG Jump if Greater than JGE Jump if Greater than or Equal JL Jump if Less than JLE Jump if Less Than or Equal JMP JuMP JNA Jump if Not Above JNAE Jump if Not Above or Equal JNB Jump if Not Below JNBE Jump if Not Below or Equal JNC Jump if No Carry JNE Jump if Not Equal JNG Jump if Not Greater than JNGE Jump if Not Greater than or Equal JNL Jump if Not Less than JNLE Jump if Not Less than or Equal JNO Jump if Not Overflow JNP Jump if Not Parity JNS Jump if Not Sign JNZ Jump if Not Zero JO Jump if Overflow JP Jump if Parity JPE Jump if Parity Even JPO Jump if Parity Odd JS Jump if Sign JZ Jump if Zero And the 80386 added: JECXZ Jump if ECX ZeroAnd by contrast, the ARM processor offers a whopping...uh...
B Branch BL Branch with LinkBut the ARM is not limited by this seemingly inflexible approach due to conditional execution which offers you:
BEQ Branch if EQual BNE Branch if Not Equal BVS Branch if oVerflow Set BVC Branch if oVerflow Clear BHI Branch if HIgher BLS Branch if Lower or the Same BPL Branch if PLus BMI Branch if MInus BCS Branch if Carry Set BCC Branch if Carry Clear BGE Branch if Greater than or Equal BGT Branch if Greater Than BLE Branch if Less than or Equal BLT Branch if Less Than BLEQ Branch with Link if EQual .... BLLT Branch with Link if Less ThanThere are two more codes,
AL
- ALways, the default condition so doesn't need to be specified
NV
- NeVer, so very useful. You should not use this code anyway...
Here follows a list of available conditional codes:
S
, when applied
to an instruction, causes the status flags to be updated. This does not happen
automatically - except for those instructions whose purpose is to set the status.
For example:
ADD R0, R0, R1 ADDS R0, R0, R1 ADDEQS R0, R0, R1The first example shows us a basic addition (adding the value of R1 to R0) which does not affect the status registers.
The second example shows us the same addition, only this time it will cause the status registers to be updated.
The last example shows us the addition again, updating the status registers. The difference here is that it is a conditional instruction. It will only be executed if the result of a previous operation was EQual (if the Z flag is set).
Here is an example of conditional execution at work. You want to compare register zero with the contents of something stored in register ten. If not equal to R10, then call a software interrupt, increment and branch back to do it again. Otherwise clear R10 and return to a calling piece of code (whose address is stored in R14).
\ An example of conditional execution .loop ; Mark the loop start position CMP R0, R10 ; Compare R0 with R10 SWINE &40017 ; Not equal: Call SWI &40017 ADDNE R0, R0, #1 ; Add 1 to R0 BNE loop ; Branch to 'loop' MOV R10, #0 ; Equal : Set R10 to zero LDMFD R13!, {R0-R12,PC} ; Return to callerNotes:
LDMFD
before, it loads multiple registers from the stack.
In this example, we are loading R0 to R12 and R14 from a fully descending stack. Refer to
str.html for more on register loading and storing.
LDMFD R13!, {R0-R12,R14}
MOV PC,
R14
MOV
statement.